<style lang="scss" scoped>
.ismcard-container {
  position: relative;
  width: 100%;
}

.ismcard-card {
  width: 100%;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
  transition: all 0.3s;
  
  &:hover {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  }
}

.ismcard-content {
  padding: 16px;
}

.ismcard-title {
  margin-bottom: 16px;
  font-size: 16px;
  font-weight: bold;
  color: #333;
  border-bottom: 1px solid #e8e8e8;
  padding-bottom: 8px;
}

.ismcard-item {
  display: flex;
  align-items: flex-start;
  margin-bottom: 8px;
  line-height: 1.6;
  
  &:last-child {
    margin-bottom: 0;
  }
}

.ismcard-key {
  color: #888;
  font-size: 14px;
  white-space: nowrap;
  min-width: 80px;
  flex-shrink: 0;
}

.ismcard-value {
  flex: 1;
  margin-left: 8px;
  color: #333;
  font-size: 14px;
  word-break: break-all;
  max-height: 120px;
  overflow-y: auto;
  
  &::-webkit-scrollbar {
    width: 6px;
    height: 6px;
  }
  
  &::-webkit-scrollbar-thumb {
    background: #ddd;
    border-radius: 3px;
    
    &:hover {
      background: #ccc;
    }
  }
  
  &::-webkit-scrollbar-track {
    background: #f5f5f5;
    border-radius: 3px;
  }
}

.ismcard-right-top {
  position: absolute;
  right: 16px;
  top: 16px;
  z-index: 10;
}

.ismcard-right-bottom {
  position: absolute;
  right: 16px;
  bottom: 16px;
  z-index: 10;
}

.ismcard-bottom {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid #e8e8e8;
}

// 响应式布局
@media (max-width: 768px) {
  .ismcard-content {
    padding: 12px;
  }
  
  .ismcard-title {
    font-size: 14px;
  }
  
  .ismcard-key,
  .ismcard-value {
    font-size: 13px;
  }
}
</style>

<template>
  <div class="ismcard-container">
    <!-- 卡片主体 -->
    <el-card class="ismcard-card" :body-style="cardBodyStyle" :shadow="cardShadow">
      <div class="ismcard-content" :style="contentStyle">
        <!-- 标题区域 -->
        <div v-if="title" class="ismcard-title">
          <slot name="title">{{ title }}</slot>
        </div>

        <!-- 自定义卡片内容 -->
        <slot name="cardWidget" v-if="$slots.cardWidget"></slot>

        <!-- 字段列表 -->
        <div v-for="fld in displayFldList" :key="fld.colname" class="ismcard-item">
          <template v-if="itemBuilder">
            <slot 
              name="itemBuilder" 
              :fld="fld" 
              :title="getFieldTitle(fld)" 
              :value="getDisplayValue(fld)"
            ></slot>
          </template>
          <template v-else>
            <!-- 自定义字段组件 -->
            <template v-if="attItems && attItems[fld.colname]">
              <div class="ismcard-key">{{ getFieldTitle(fld) }}：</div>
              <div class="ismcard-value">
                <slot :name="fld.colname" :fld="fld" :value="getDisplayValue(fld)">
                  {{ attItems[fld.colname] }}
                </slot>
              </div>
            </template>
            <!-- 默认字段显示 -->
            <template v-else>
              <div class="ismcard-key" :style="keyTextStyle">
                {{ getFieldTitle(fld) }}：
              </div>
              <div class="ismcard-value" :style="getValueStyle(fld)">
                {{ getDisplayValue(fld) }}
              </div>
            </template>
          </template>
        </div>

        <!-- 底部内容 -->
        <div v-if="$slots.bottomWidget" class="ismcard-bottom">
          <slot name="bottomWidget"></slot>
        </div>
      </div>
    </el-card>

    <!-- 右上角内容 -->
    <div v-if="$slots.rightTop" class="ismcard-right-top">
      <slot name="rightTop"></slot>
    </div>

    <!-- 右下角内容 -->
    <div v-if="$slots.rightBottom" class="ismcard-right-bottom">
      <slot name="rightBottom"></slot>
    </div>

    <!-- 自定义定位内容 -->
    <slot name="children"></slot>
  </div>
</template>

<script>
/**
 * 组件名称: Ismcard (Element UI 版本)
 * 功能说明: 信息卡片展示组件，用于展示结构化数据
 * 研发时间: 2025/11/17
 * 研发人员: AI Assistant
 * 
 * 基于 cx2-ismcard 改造为 Element UI 版本，适配桌面端
 */

/**
 * 修改记录:
 * 2025-07-31     郑朝军        创建ismcard组件
 * 2025-11-17     AI Assistant  改造为 Element UI 版本
 */

export default {
  name: 'Ismcard',

  /**
   * 参数说明:
   * @prop {Number}      [major]                 主类型
   * @prop {Number}      [minor]                 子类型
   * @prop {Array}       [fldList]               外部自定义传入的字段集合
   * @prop {String}      [tabCfgName]            通过tabCfg配置显示的字段名称
   * @prop {String}      [title]                 卡片标题
   * @prop {Object}      attValue                属性值对象
   * @prop {Object}      [keyTextStyle]          键的文字样式
   * @prop {Object}      [valueTextStyle]        值的文字样式
   * @prop {Object}      [attItems]              自定义值的布局映射
   * @prop {String}      [elevation]             卡片阴影类型: always/hover/never
   * @prop {String}      [color]                 卡片背景色
   * @prop {Number}      [padding]               卡片内边距
   * @prop {Boolean}     [itemBuilder]           是否使用自定义行构造器
   */
  props: {
    major: { type: Number, default: null },
    minor: { type: Number, default: null },
    fldList: { type: Array, default: () => [] },
    tabCfgName: { type: String, default: null },
    title: { type: String, default: '' },
    attValue: { type: Object, required: true },
    keyTextStyle: { 
      type: Object, 
      default: () => ({ color: '#888888', fontSize: '14px' }) 
    },
    valueTextStyle: { 
      type: Object, 
      default: () => ({ color: '#333333', fontSize: '14px' }) 
    },
    attItems: { type: Object, default: () => ({}) },
    elevation: { type: String, default: 'hover' }, // always/hover/never
    color: { type: String, default: '#ffffff' },
    padding: { type: Number, default: 16 },
    itemBuilder: { type: Boolean, default: false }
  },

  /**
   * @data         { Array }       displayFldList        要显示的字段
   * @data         { Object }      fldValueMap           fldvalue集合对象
   * @data         { Object }      tabCfg                tabCfg表的对象
   */
  data() {
    return {
      displayFldList: [],
      fldValueMap: {},
      tabCfg: null
    }
  },

  computed: {
    /**
     * 卡片阴影类型
     */
    cardShadow() {
      return this.elevation
    },

    /**
     * 卡片主体样式
     */
    cardBodyStyle() {
      return {
        backgroundColor: this.color,
        padding: 0
      }
    },

    /**
     * 内容区域样式
     */
    contentStyle() {
      return {
        padding: `${this.padding}px`
      }
    }
  },

  mounted() {
    this.initData()
  },

  methods: {
    /**
     * 初始化组件数据，根据配置加载字段信息
     * @async
     * @method                      initData
     * @description                 支持通过tabCfgName或major/minor两种方式初始化字段数据
     */
    async initData() {
      try {
        let tabName = ''

        // 通过tabCfgName获取配置
        if (this.tabCfgName) {
          this.tabCfg = await this.getTabCfg(this.tabCfgName)
          if (!this.tabCfg) {
            console.error(`cx_tabcfg中没有配置${this.tabCfgName}或cx_tabCfg未缓存`)
            return
          }
          tabName = this.tabCfg.tab
          const showCols = this.tabCfg.disp.split(',')
          // 根据配置过滤显示字段
          this.displayFldList = this.fldList.length > 0
            ? this.fldList.filter(e => showCols.includes(e.colname))
            : await this.getFldList(tabName, showCols)
        }
        // 通过major/minor获取实体
        else if (this.major !== null && this.minor !== null) {
          const entity = await this.getEntity(this.major, this.minor)

          if (!entity) {
            console.error(`major:${this.major},minor:${this.minor}表不存在`)
            return
          }
          tabName = entity.name
          // 使用传入的字段列表或查询卡片字段列表
          this.displayFldList = this.fldList.length > 0
            ? this.fldList
            : await this.getCardFldList(tabName)
        }

        // 获取字段值映射表，用于字典值转换
        this.fldValueMap = await this.getFldValueMap(tabName)

        // 如果本地没有字段数据，从服务器异步加载
        await this.loadFldFromSvr(tabName)
      } catch (error) {
        console.error('初始化数据失败:', error)
      }
    },

    /**
     * 从服务器批量加载字段定义和字段值数据
     * @async
     * @param                   {string}        tabName           表名
     * @description                                               当本地字段列表为空时，从服务器获取字段定义和字典值
     */
    async loadFldFromSvr(tabName) {
      // 只有当显示字段列表为空时才从服务器加载
      if (this.displayFldList.length === 0) {
        try {
          // 批量查询字段定义和字段值
          const result = await cx.DataQuery.batchQueryFldAndValue(tabName)

          // 过滤需要显示的字段
          this.displayFldList = result.flds.filter(fld => {
            if (this.tabCfg) {
              // 根据tabCfg配置过滤字段
              return this.tabCfg.disp.split(',').includes(fld.colname)
            }
            // 默认显示标记为卡片显示的字段
            const paramsIsmcard = fld.params?.ismcard
            const directIsmcard = fld.ismcard

            return paramsIsmcard === 1 || directIsmcard === 1
          })

          // 更新字段值映射表
          this.fldValueMap = result.vals
        } catch (error) {
          console.error('从服务器加载字段数据失败:', error)
        }
      }
    },

    /**
     * 获取字段显示标题，包含单位信息
     * @param         {Object}          fld          字段对象
     * @param         {string}          fld.namec    字段中文名称
     * @param         {string}          [fld.unit]   字段单位
     * @returns       {string}                       格式化后的字段标题
     * @example
     * // 返回: "温度(℃)"
     * getFieldTitle({namec: '温度', unit: '℃'})
     */
    getFieldTitle(fld) {
      // 如果有单位且不为空，则添加单位显示
      const unit = fld.unit && fld.unit.trim() ? `(${fld.unit})` : ''
      return `${fld.namec}${unit}`
    },

    /**
     * 获取字段的显示值，支持格式化和字典转换
     *
     * @param           {Object}        fld             字段对象
     * @param           {string}        fld.colname     字段列名
     * @param           {string}        [fld.format]    字段格式化规则
     * @param           {number}        [fld.disptype]  字段显示类型
     * @returns         {string}                        格式化后的显示值
     * @description                                     处理数据格式化、字典值转换、多选分隔符替换等
     */
    getDisplayValue(fld) {
      // 获取数据库原始值
      const dbValue = this.attValue[fld.colname]?.toString() || ''

      // 尝试按格式化规则处理数据
      const formatValue = this.formatData(fld.format, dbValue)

      let displayValue = dbValue
      if (!formatValue && dbValue) {
        // 查询字典显示值，将编码转换为显示文本
        displayValue = this.queryDispC(fld, dbValue) || dbValue
      }

      // 处理多选类型字段，将分隔符替换为逗号
      if (cx && cx.consts && fld.disptype === cx.consts.DISP_MULTISEL) {
        displayValue = displayValue.replace(new RegExp(cx.consts.TEXT_SEPARATOR, 'g'), ',')
      }

      return displayValue ?? formatValue ?? ''
    },

    /**
     * 获取字段值的显示样式，支持视觉颜色
     * @param           {Object} fld    字段对象
     * @returns         {Object}        样式对象
     * @description                     基于基础样式，根据字段值动态设置颜色
     */
    getValueStyle(fld) {
      // 复制基础样式对象
      const baseStyle = { ...this.valueTextStyle }

      // 根据字段配置和当前值获取视觉颜色
      const visualColor = this.getVisualColor(fld, this.attValue[fld.colname])
      if (visualColor) {
        baseStyle.color = visualColor
      }
      return baseStyle
    },

    /**
     * 查询字段的字典显示值
     * @param         {Object}          fld            字段对象
     * @param         {string}          fld.tabname    表名
     * @param         {string}          fld.colname    列名
     * @param         {string|number}   dbValue        数据库存储值
     * @returns       {string|null}                    字典显示值，未找到返回null
     * @description                                    将数据库中的编码值转换为用户友好的显示文本
     */
    queryDispC(fld, dbValue) {
      if (cx && cx.fldvalue) {
        return cx.fldvalue.queryFldValueDispc(fld.tabname, fld.colname, dbValue)
      }
      return null
    },

    /**
     * 根据格式化规则处理数据
     * @param         {string}            format  格式化规则字符串
     * @param         {string|number}     value   原始值
     * @returns       {string|null}               格式化后的值，无法格式化返回null
     * @description                               支持日期、数字、文本等多种格式化规则
     */
    formatData(format, value) {
      if (cx && cx.Datafmt) {
        return cx.Datafmt.format(format, value)
      }
      return null
    },

    /**
     * 获取字段值对应的视觉颜色
     * @param           {Object}        fld      字段对象
     * @param           {string|number} value    字段值
     * @returns         {string|null}            颜色值，无特殊颜色返回null
     * @description                              根据字段配置和值的范围返回对应的显示颜色
     */
    getVisualColor(fld, value) {
      if (cx && cx.DataVsl) {
        return cx.DataVsl.visual(fld, value)
      }
      return null
    },

    /**
     * 根据配置名称获取表配置信息
     * @param       {string}                         tabCfgName       表配置名称
     * @returns     {Promise<Object|undefined>}                       表配置对象，未找到返回undefined
     * @description                                                   从缓存的表配置列表中查找指定名称的配置
     */
    async getTabCfg(tabCfgName) {
      if (cx && cx.InitSvrMethod) {
        return cx.InitSvrMethod.getTabcfg().find(e => e.name === tabCfgName)
      }
      return null
    },

    /**
     * 根据主次类型获取实体信息
     * @param             {number}            major    主类型
     * @param             {number}            minor    次类型
     * @returns           {Promise<Object|null>}       实体对象，未找到返回null
     * @description                                    通过实体管理器查询指定类型的实体定义
     */
    async getEntity(major, minor) {
      if (cx && cx.entity) {
        return cx.entity.queryEntity(major, minor)
      }
      return null
    },

    /**
     * 获取指定表的字段列表并按列名过滤
     * @param           {string}        tabName    表名
     * @param           {string[]}      showCols   需要显示的列名数组
     * @returns         {Promise<Object[]>}        过滤后的字段对象数组
     * @description                                从字段管理器获取全部字段，然后按指定列名过滤
     */
    async getFldList(tabName, showCols) {
      if (cx && cx.fld) {
        const allFlds = cx.fld.queryFldList(tabName) || []
        return allFlds.filter(e => showCols.includes(e.colname))
      }
      return []
    },

    /**
     * 获取指定表的卡片显示字段列表
     * @param         {string}              tabName       表名
     * @returns       {Promise<Object[]>}                 卡片字段对象数组
     * @description                                       获取标记为卡片显示的字段列表
     */
    async getCardFldList(tabName) {
      if (cx && cx.fld) {
        return cx.fld.queryCardFldList(tabName) || []
      }
      return []
    },

    /**
     * 获取指定表的字段值映射表
     * @param       {string}              tabName       表名
     * @returns     {Promise<Object>}                   字段值映射对象，key为字段名，value为字典值数组
     * @description                                     用于字段值的字典转换，提供编码到显示文本的映射
     */
    async getFldValueMap(tabName) {
      if (cx && cx.fldvalue) {
        return cx.fldvalue.queryTabFldValueListMap(tabName) || {}
      }
      return {}
    }
  }
}
</script>
